//
// A simple wrapper of the POSIX thread functions in C++,
// since it doesn't provide for the more complex calls,
// it leaves the underlying Pthreads public.

#include <pthread.h>
#include <errno.h>

namespace pw // POSIX wrapped
{

// [[ex01aa]]
// DEBUG descr POSIX wrapper mutex class.
class pmutex 
{
public:
	pmutex(const pthread_mutexattr_t *attr = 0)
	{
		pthread_mutex_init(&mutex_, attr);
	}
	~pmutex()
	{
		pthread_mutex_destroy(&mutex_);
	}
	int lock()
	{
		return pthread_mutex_lock(&mutex_);
	}
	int trylock()
	{
		return pthread_mutex_trylock(&mutex_);
	}
	int unlock()
	{
		return pthread_mutex_unlock(&mutex_);
	}

	pthread_mutex_t mutex_;
};
// END

// a simple scoped lock and unlock
// [[ex01ab]]
// DEBUG descr POSIX wrapper scoped lock and unlock classes.
class lockmutex
{
public:
	lockmutex(pmutex &m) :
		mutex_(m)
	{
		mutex_.lock();
	}
	~lockmutex()
	{
		mutex_.unlock();
	}
protected:
	pmutex &mutex_;
};

class unlockmutex
{
public:
	unlockmutex(pmutex &m) :
		mutex_(m)
	{
		mutex_.unlock();
	}
	~unlockmutex()
	{
		mutex_.lock();
	}
protected:
	pmutex &mutex_;
};
// END

// [[ex01ac]]
// DEBUG descr POSIX wrapper mutex chaining class.
class swapmutex
{
public:
	swapmutex(pmutex &mold, pmutex &mnew) :
		mutexold_(mold), mutexnew_(mnew)
	{
		mutexnew_.lock();
		mutexold_.unlock();
	}
	~swapmutex()
	{
		mutexnew_.unlock();
		mutexold_.lock();
	}
protected:
	pmutex &mutexold_, &mutexnew_;
};
// END

// [[ex01ad]]
// DEBUG descr POSIX wrapper condition variable class.
class pcond
{
public:
	pcond(const pthread_condattr_t *attr = 0)
	{
		pthread_cond_init(&cond_, attr);
	}
	~pcond()
	{
		pthread_cond_destroy(&cond_);
	}
	int wait(pmutex *mutex)
	{
		return pthread_cond_wait(&cond_, 
			&mutex->mutex_);
	}
	int timedwait(pmutex *mutex, 
		const struct timespec &abstime)
	{
		return pthread_cond_timedwait(&cond_, 
			&mutex->mutex_, &abstime);
	}
	// expects that the mutex is already locked
	int signal()
	{
		return pthread_cond_signal(&cond_);
	}
	int broadcast()
	{
		return pthread_cond_broadcast(&cond_);
	}

	pthread_cond_t cond_;
};
// END

// a typical combination of a condition variable with a mutex and number of
// sleepers 
// (note that since it inherits from pmutex, the autolock/autounlock
// can be applied to it too!)
// [[ex01ae]]
// DEBUG descr POSIX wrapper condition variable class combined with a mutex.
class pmcond: public pmutex, public pcond
{
public:
	pmcond(const pthread_mutexattr_t *mattr = 0, 
			const pthread_condattr_t *cattr = 0) :
		pmutex(mattr), pcond(cattr),
		sleepers_(0)
	{ }
	// wait with the built-in mutex locked on entry
	// and exit (note that the sleepers counter
	// can't be kept right if using a pcond::wait
	// with a different mutex)
	int wait()
	{
		++sleepers_;
		int res = pcond::wait(this);
		return res;
	}
	int timedwait(const struct timespec &abstime)
	{
		++sleepers_;
		int res = pcond::timedwait(this, abstime);
		// don't decrease sleepers_, since it would 
		// cause a race
		return res;
	}

	// convenience wrappers that avoid the signaling
	// if there are no sleepers
	int signal()
	{
		int res = 0;
		if (sleepers_ > 0) {
			res = pthread_cond_signal(&cond_);
			--sleepers_;
		}
		return res;
	}
	int broadcast()
	{
		int res = 0;
		if (sleepers_ > 0) {
			res = pthread_cond_broadcast(&cond_);
			sleepers_ = 0;
		}
		return res;
	}
	// convenience wrappers that get the mutex 
	// before signaling
	int signallock()
	{
		lock();
		int res = signal();
		unlock();
		return res;
	}
	int broadcastlock()
	{
		lock();
		int res = broadcast();
		unlock();
		return res;
	}

	int sleepers_;
};
// END

// essentially a copy of pmcond but with an externally supplied mutex
// (that possibly belongs to some pmcond instance), this allows to chain
// multiple conditions off the same mutex
// [[ex01af]]
// DEBUG descr POSIX wrapper condition variable class sharing the mutex of another condition variable.
class pchaincond: public pcond
{
public:
	pchaincond(pmutex &mutex, 
			const pthread_condattr_t *cattr = 0) :
		pcond(cattr),
		mutex_(mutex), sleepers_(0)
	{ }
	int wait()
	{
		++sleepers_;
		int res = pcond::wait(&mutex_);
		return res;
	}
	int timedwait(const struct timespec &abstime)
	{
		++sleepers_;
		int res = pcond::timedwait(&mutex_, abstime);
		return res;
	}

	// convenience wrappers that avoid the signaling
	// if there are no sleepers
	int signal()
	{
		int res = 0;
		if (sleepers_ > 0) {
			res = pthread_cond_signal(&cond_);
			--sleepers_;
		}
		return res;
	}
	int broadcast()
	{
		int res = 0;
		if (sleepers_ > 0) {
			res = pthread_cond_broadcast(&cond_);
			sleepers_ = 0;
		}
		return res;
	}
	// convenience wrappers that get the mutex
	// before signaling
	int signallock()
	{
		mutex_.lock();
		int res = signal();
		mutex_.unlock();
		return res;
	}
	int broadcastlock()
	{
		mutex_.lock();
		int res = broadcast();
		mutex_.unlock();
		return res;
	}

	pmutex &mutex_;
	int sleepers_;
};
// END

// [[ex01ag]]
// DEBUG descr POSIX wrapper read-write lock class.
class prwlock 
{
public:
	prwlock(const pthread_rwlockattr_t *attr = 0)
	{
		pthread_rwlock_init(&rwlock_, attr);
	}
	~prwlock()
	{
		pthread_rwlock_destroy(&rwlock_);
	}
	int wrlock()
	{
		return pthread_rwlock_wrlock(&rwlock_);
	}
	int trywrlock()
	{
		return pthread_rwlock_trywrlock(&rwlock_);
	}
	int timedwrlock(const struct timespec &abstime)
	{
		return pthread_rwlock_timedwrlock(&rwlock_, 
			&abstime);
	}
	int rdlock()
	{
		return pthread_rwlock_rdlock(&rwlock_);
	}
	int tryrdlock()
	{
		return pthread_rwlock_tryrdlock(&rwlock_);
	}
	int timedrdlock(const struct timespec &abstime)
	{
		return pthread_rwlock_timedrdlock(&rwlock_, 
			&abstime);
	}
	int unlock()
	{
		return pthread_rwlock_unlock(&rwlock_);
	}

	pthread_rwlock_t rwlock_;
};

// a simple scoped lock and unlock for 
// read and write
class lockwr
{
public:
	lockwr(prwlock &l) :
		rwlock_(l)
	{
		rwlock_.wrlock();
	}
	~lockwr()
	{
		rwlock_.unlock();
	}
protected:
	prwlock &rwlock_;
};
class unlockwr
{
public:
	unlockwr(prwlock &l) :
		rwlock_(l)
	{
		rwlock_.unlock();
	}
	~unlockwr()
	{
		rwlock_.wrlock();
	}
protected:
	prwlock &rwlock_;
};
class lockrd
{
public:
	lockrd(prwlock &l) :
		rwlock_(l)
	{
		rwlock_.rdlock();
	}
	~lockrd()
	{
		rwlock_.unlock();
	}
protected:
	prwlock &rwlock_;
};
class unlockrd
{
public:
	unlockrd(prwlock &l) :
		rwlock_(l)
	{
		rwlock_.unlock();
	}
	~unlockrd()
	{
		rwlock_.rdlock();
	}
protected:
	prwlock &rwlock_;
};
// END

// [[ex01ah]]
// DEBUG descr POSIX wrapper thread class.
class pwthread
{
public:
	pwthread(bool detached = false, 
			pthread_attr_t *attr = 0) :
		detached_(detached), attr_(attr), id_(0)
	{ }
	virtual ~pwthread()
	{ }

	void set_attr(pthread_attr_t *attr)
	{
		attr_ = attr;
	}

	pthread_t get_id()
	{
		return id_;
	}

	virtual void *execute()
	{
		return 0;
	}

	// makes no checks if the thread is already
	// running, the caller must be careful
	void start()
	{
		start_mutex_.lock();
		pthread_create(&id_, attr_, run_it, 
			(void *)this);
		if (detached_)
			pthread_detach(id_);
		start_mutex_.unlock();
	}

	// join may be called only once for non-detached
	// threads, and  only after the thread had been
	// started
	void *join()
	{
		void *result;
		pthread_join(id_, &result);
		return result;
	}

protected:
	static void *run_it(void *arg)
	{
		pwthread *t = (pwthread *)arg;

		t->start_mutex_.lock();
		t->start_mutex_.unlock();

		void *result = t->execute();
		if (t->detached_) {
			// must delete own object since nobody else
			// can collect it
			delete t;
		}
		return result;
	}

	bool detached_;
	pthread_attr_t *attr_;
	pthread_t id_;
	pmutex start_mutex_;
};
// END

}; // pw
